﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using Dimebrain.TweetSharp.Core.Extensions;

namespace Dimebrain.TweetSharp.Core.Web.Query.OAuth
{
    // todo implement OAuth 
    internal class OAuthWebQueryClient : IWebQueryClient
    {
        public WebResponse Response { get; private set; }
        public WebRequest Request { get; private set; }
        public ICredentials Credentials { get; set; }
        public bool UseCompression { get; set; }
        public string ProxyValue { get; set; }

        private readonly IDictionary<string, string> _headers;
        public WebParameterCollection Parameters { get; private set; }
        public string UserAgent { get; private set; }

        public OAuthWebQueryClient(IDictionary<string, string> headers, WebParameterCollection parameters, string userAgent)
        {
            _headers = headers;
            Parameters = parameters;
            UserAgent = userAgent;
        }

        public WebRequest GetWebRequestShim(Uri address)
        {
            var request = (HttpWebRequest) WebRequest.Create(address);

            /*if (_headers != null)
            {
                foreach (var header in _headers)
                {
                    request.Headers[header.Key] = header.Value;
                }
            }

            if (UseCompression)
            {
                request.Accept = "gzip,deflate";
            }

            if (Credentials != null)
            {
                var credentials = (NetworkCredential) Credentials;
                request.Headers["Authorization"] =
                    credentials.ToAuthorizationHeader();
            }

            if (Parameters != null)
            {
                var hasParameters = address.Query.Contains("?");
                foreach (var parameter in Parameters)
                {
                    address.Query.Then(hasParameters ? "&" : "?");
                    address.Query.Then("{0}={1}".FormatWith(parameter.Name, parameter.Value));
                    hasParameters = true;
                }
            }

            if (!UserAgent.IsNullOrBlank())
            {
                request.Headers["User-Agent"] = UserAgent;
            }*/

            // NOTE: Copied from WebQueryClient.GetWebRequestShim()
            // You can't set headers in a GET request in Silverlight,
            // so override to POST in all cases and use the 'X-Twitter-Method'
            // header value to let the proxy know it should be a GET.
            request.Method = "POST";
            request.ContentType = "application/x-www-form-urlencoded";

            request.Headers["X-Twitter-Method"] = "GET";
            if (_headers != null)
            {
                foreach (var header in _headers)
                {
                    request.Headers[header.Key] = header.Value;
                }
            }

            if (UseCompression)
            {
                request.Headers["X-Twitter-Accept"] = "gzip,deflate";
            }

            if (Credentials != null)
            {
                var credentials = (NetworkCredential)Credentials;
                request.Headers["X-Twitter-Auth"] = credentials.ToAuthorizationHeader();
            }

            if (!UserAgent.IsNullOrBlank())
            {
                request.Headers["X-Twitter-Agent"] = UserAgent;
            }

            if (Parameters != null)
            {
                var hasParameters = address.Query.Contains("?");
                foreach (var parameter in Parameters)
                {
                    address.Query.Then(hasParameters ? "&" : "?");
                    address.Query.Then("{0}={1}".FormatWith(parameter.Name, parameter.Value));
                    hasParameters = true;
                }
            }

            SetAuthorizationHeader(request);

            Request = request;
            return request;
        }

        protected void SetAuthorizationHeader(WebRequest request) // Note: Adapted from OAuthWebQuery.SetAuthorizationHeader()
        {
            var sb = new System.Text.StringBuilder("OAuth ");

            var parameters = 0;
            foreach (var parameter in Parameters)
            {
                if(parameter.Name.IsNullOrBlank() || parameter.Value.IsNullOrBlank())
                {
                    continue;
                }

                parameters++;
                var format = parameters < Parameters.Count ? "{0}=\"{1}\"," : "{0}=\"{1}\"";
                sb.Append(format.FormatWith(parameter.Name, parameter.Value));
            }

            var authorization = sb.ToString();

            request.Headers["X-Twitter-Auth"] = authorization;
        }

        public WebResponse GetWebResponseShim(WebRequest request, IAsyncResult result)
        {
            try
            {
                Response = request.EndGetResponse(result);
                return Response;
            }
            catch (WebException ex)
            {
                return HandleWebException(ex);
            }
        }

        public event EventHandler<OpenReadCompletedEventArgs> OpenReadCompleted;

        public void OnOpenReadCompleted(Stream result)
        {
            if (OpenReadCompleted == null)
            {
                return;
            }
            var args = new OpenReadCompletedEventArgs(result);
            OpenReadCompleted(this, args);
        }

        public void OpenReadAsync(Uri uri)
        {
            var request = GetWebRequestShim(uri);

            request.BeginGetResponse(BeginGetResponseCompleted, request);
        }

        public void OpenReadAsync(Uri uri, object state)
        {
            var request = GetWebRequestShim(uri);
            var pair = new Pair<WebRequest, object>
                           {
                               First = request,
                               Second = state
                           };

            request.BeginGetResponse(BeginGetResponseCompleted, pair);
        }

        private void BeginGetResponseCompleted(IAsyncResult ar)
        {
            WebRequest request;
            if(ar.AsyncState is WebRequest)
            {
                request = (WebRequest)ar.AsyncState;
            }
            else
            {
                var pair = (Pair<WebRequest, object>) ar.AsyncState;
                request = pair.First;
            }

            Response = request.EndGetResponse(ar);
            var stream = Response.GetResponseStream();
            OnOpenReadCompleted(stream);
        }

        private static WebResponse HandleWebException(WebException ex)
        {
            if (ex.Response != null && ex.Response is HttpWebResponse)
            {
                return ex.Response;
            }

            // not the droids we're looking for
            throw ex;
        }
    }
}
